package com.learncloud.service.impl;


import com.learncloud.common.FFmpeg.MediaUtil;
import com.learncloud.common.FFmpeg.domain.VideoMetaInfo;
import com.learncloud.common.config.MinioConfigParams;
import com.learncloud.common.core.exception.ServiceException;
import com.learncloud.common.core.util.HttpUtils;
import com.learncloud.common.util.FileUtil;
import com.learncloud.entity.FileShardInfo;
import com.learncloud.service.IFileService;
import io.minio.MinioClient;
import io.minio.Result;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author : yj
 */
@Service
@Slf4j
public class FileServiceImpl implements IFileService {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfigParams minioConfigParams;

    @Autowired
    MediaUtil mediaUtil;


    @Override
    public String upload(MultipartFile file) {
        String contentType = file.getContentType();
        String objectUrl = null;
        String[] split = contentType.split("/");
        try {
            String fileName = getFileName(split[0], split[1]);
            minioClient.putObject(checkBucket(split[0]), fileName, file.getInputStream(), file.getContentType());
            objectUrl = minioClient.getObjectUrl(minioConfigParams.getImgBucket(), fileName);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return objectUrl;
    }

    @Override
    public Map<String, List<FileShardInfo>> fragmentUploadUrls(MultipartFile file) {
//        SysUserDetails userDetails = SysUserUtils.getUserDetails();
//        Integer id = userDetails.getSysUser().getId();
        UUID uuid = UUID.randomUUID();
        BigDecimal divide = new BigDecimal(file.getSize()).divide(new BigDecimal(minioConfigParams.getFragmentSize()));
        double ceil = Math.ceil(divide.doubleValue());
        int totalShard = (int) ceil;
        List<FileShardInfo> urls = new ArrayList<>();
        StringBuilder stringBuilder = new StringBuilder().append(minioConfigParams.getReadOnlyPrefix())
//                .append(id)
                .append("-").append(uuid);
        String objectNamePrefix = stringBuilder.toString();
        for (int i = 1; i <= totalShard; i++) {
            FileShardInfo shardInfo = new FileShardInfo();
            try {
                String url = minioClient.presignedPutObject(minioConfigParams.getTemporaryStorage(), objectNamePrefix + "/" + i, 60 * 60 * 24);
                shardInfo.setUrl(url).setStart(getStart(i)).setEnd(getEnd(i, totalShard, file.getSize()));
                urls.add(shardInfo);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Map map = new HashMap();
        map.put("urls", urls);
        map.put("objectName", objectNamePrefix);
        map.put("bucketName", minioConfigParams.getTemporaryStorage());
        return map;
    }

    @Override
    public VideoMetaInfo mergeAndTranscode(String objectName, String type) {
        VideoMetaInfo videoMetaInfo = null;
        File file = mergeFile(objectName, type);
        String path;
        try {
            Map map = transcodeFile(file, type);
            path = String.valueOf(map.get("path"));
            videoMetaInfo = (VideoMetaInfo) map.get("videoMetaInfo");
        } catch (Exception e) {
            FileUtil.deleteDir(minioConfigParams.getLocalFilePrefix() + objectName);
            throw new ServiceException(e.getMessage());
        }
        File dir = new File(path);
        File[] files = dir.listFiles();
        for (File f : files) {
            String s1 = objectName + "/" + f.getParentFile().getName() + "/" + f.getName();
            try {
                FileInputStream fileInputStream = new FileInputStream(f);
                minioClient.putObject(minioConfigParams.getTemporaryStorage(), s1, fileInputStream, "");
                if ("video.m3u8".equals(f.getName())) {
                    videoMetaInfo.setUrl(minioClient.getObjectUrl(minioConfigParams.getTemporaryStorage(), s1));
                    videoMetaInfo.setBucketName(minioConfigParams.getTemporaryStorage());
                    videoMetaInfo.setObjectName(s1);
                }
                fileInputStream.close();
            } catch (Exception e) {
                throw new ServiceException(e.getMessage());
            }
        }
        FileUtil.deleteDir(minioConfigParams.getLocalFilePrefix() + objectName);
        return videoMetaInfo;
    }

    @Override
    public void delete(String bucketName, String objectName) {
        try {
            Iterable<Result<Item>> myObjects = minioClient.listObjects(bucketName, objectName);
            for (Result<Item> result : myObjects) {
                minioClient.removeObject(bucketName, result.get().objectName());
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new ServiceException("删除失败");
        }
    }

    @Override
    public VideoMetaInfo uploadByM3u8Url(String mau8Url) {
        VideoMetaInfo videoMetaInfo = new VideoMetaInfo();
        byte[] bytes = null;
        bytes = HttpUtils.doGet(mau8Url);
        if (bytes == null) {
            throw new ServiceException("获取m3u8文件失败");
        }
        String s = new String(bytes);
        UUID uuid = UUID.randomUUID();
        StringBuffer objectNamePrefix = new StringBuffer(minioConfigParams.getReadOnlyPrefix()).append(uuid.toString()).append("/m3u8");
        String m3u8ObjectName = new StringBuffer(objectNamePrefix.toString()).append(mau8Url.substring(mau8Url.lastIndexOf("/"),mau8Url.lastIndexOf("?"))).toString();
        ByteArrayInputStream byteInputStream = new ByteArrayInputStream(bytes);
        try {
            minioClient.putObject(minioConfigParams.getTemporaryStorage(),m3u8ObjectName,byteInputStream,"m3u8");
            String objectUrl = minioClient.getObjectUrl(minioConfigParams.getTemporaryStorage(), m3u8ObjectName);
            videoMetaInfo.setObjectName(m3u8ObjectName);
            videoMetaInfo.setBucketName(minioConfigParams.getTemporaryStorage());
            videoMetaInfo.setUrl(objectUrl);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("上传视频文件失败");
        }
        String[] split = s.split("\n");
        String prefixUrl = mau8Url.substring(0,mau8Url.lastIndexOf("/")+1);
        String videoUrl  = null;
        String tsObjectName = null;
        for (int i = 0 ;i<split.length; i++){
            String suffix = split[i];
            if (!"#".equals(suffix.substring(0,1))){
                videoUrl = prefixUrl.concat(suffix);
                bytes = HttpUtils.doGet(videoUrl);
                byteInputStream = new ByteArrayInputStream(bytes);
                tsObjectName = new StringBuffer(objectNamePrefix.toString()).append(videoUrl.substring(videoUrl.lastIndexOf("/"),videoUrl.lastIndexOf("?"))).toString();
                try {
                    minioClient.putObject(minioConfigParams.getTemporaryStorage(),tsObjectName,byteInputStream,"ts");
                } catch (Exception e) {
                    e.printStackTrace();
                    delete(videoMetaInfo.getBucketName(),videoMetaInfo.getObjectName());
                    throw new ServiceException("上传视频文件失败");
                }
            }
        }
        return videoMetaInfo;
    }

    // 转m3u8
    private Map transcodeFile(File inFile, String type) {
        if (inFile == null || !inFile.exists()) {
            return null;
        }
        String directoryPath = inFile.getParentFile().getPath();
        VideoMetaInfo videoMetaInfo = mediaUtil.getVideoMetaInfo(inFile);
        String m3u8DirPath = directoryPath + File.separator + "m3u8";
        File m3u8File = new File(m3u8DirPath);
        if (!m3u8File.exists()) {
            m3u8File.mkdirs();
        }
        mediaUtil.processM3U8(inFile.getPath(), m3u8DirPath, "video");
        Map map = new HashMap();
        map.put("path", m3u8DirPath);
        map.put("videoMetaInfo", videoMetaInfo);
        return map;
    }

    // 从minio获取文件合并
    private File mergeFile(String objectName, String type) {
        boolean isError = false;
        Item item = null;
        FileOutputStream fileOutputStream = null;
        InputStream inputStream = null;
        File dir = new File(minioConfigParams.getLocalFilePrefix() + File.separator + objectName);
        String outFilePath = dir.getPath() + "/base." + type;
        byte buffer[] = new byte[1024 * 1024];//一次读取2M数据，将读取到的数据保存到byte字节数组中
        int len = 0;
        // 一、检查放置文件的文件夹路径是否存在，不存在则创建
        if (!dir.exists()) {
            dir.mkdirs();// mkdirs创建多级目录
        }
        try {
            fileOutputStream = new FileOutputStream(outFilePath);
            Iterable<Result<Item>> myObjects = minioClient.listObjects(minioConfigParams.getTemporaryStorage(), objectName);
            Iterator<Result<Item>> iterator = myObjects.iterator();
            Map<Integer,String> map = new HashMap<>();
            while (iterator.hasNext()) {
                Result<Item> result = iterator.next();
                item = result.get();
                String[] split = item.objectName().split("/");
                map.put(Integer.valueOf(split[split.length-1]),item.objectName());
            }
            Set<Integer> strings = map.keySet();
            for (Integer key : strings){
                String childObjectName = map.get(key);
                inputStream = minioClient.getObject(minioConfigParams.getTemporaryStorage(), childObjectName);
                while ((len = inputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer,0,len);//buffer从指定字节数组写入。buffer:数据中的起始偏移量,le入的n:写字数。
                }
                inputStream.close();
            }
            fileOutputStream.flush();
        } catch (Exception e) {
            log.error("合并转码视频{}失败,错误信息{}", item.objectName(), e.getMessage());
            isError = true;
        } finally {
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        File file = new File(outFilePath);
        if (isError) {
            if (dir.exists()) {
                FileUtil.deleteDir(dir);
            }
            return null;
        }
        return file;
    }

    private Long getStart(Integer index) {
        Long start = 0l;
        if (index > 1) {
            start = (index - 1) * minioConfigParams.getFragmentSize();
        }
        return start;
    }

    private Long getEnd(Integer index, Integer endIndex, Long totalSize) {
        Long end = this.minioConfigParams.getFragmentSize() * index;
        if (index.equals(endIndex)) {
            end = totalSize;
        }
        return end;
    }

    ;

    /**
     * 根据文件类型选择存储桶
     *
     * @param type
     * @return
     */
    private String checkBucket(String type) {
        String bucket;
        if (minioConfigParams.getImgBucket().equals(type)) {
            bucket = minioConfigParams.getImgBucket();
        } else if (minioConfigParams.getVideoBucket().equals(type)) {
            bucket = minioConfigParams.getVideoBucket();
        } else {
            bucket = "other";
        }
        return bucket;
    }

    /**
     * 生成文件名称
     *
     * @param type
     * @return
     */
    private String getFileName(String type, String suffix) {
        UUID uuid = UUID.randomUUID();
        StringBuffer buffer = new StringBuffer();
        if (type.equals("image")) {
            buffer = buffer.append(minioConfigParams.getReadOnlyPrefix());
        }
        buffer = buffer.append(uuid.toString()).append(suffix);
        return buffer.toString();
    }

}
